﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace MyUtils.RedisService.RealisationCases
{

    /*
     RESP协议:
        *2  (命令开始标记 后面数字为本次argv的数量)
        $3  (argv[0] 的 长度)
        GET (argv[0])
        $1  (argv[1] 的 长度)
        a (argv[1])
    e.g.:
        *3
        $3
        get
        $1
        a

    e.g.:
        *3
        $3
        set
        $1
        a
        $3
        acc

     */
    internal class RedisClone
    {
        ConcurrentDictionary<string, string> _state = new();

        /// <summary>
        /// 处理链接
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        public async Task HandleConnection(TcpClient client)
        {
            using var _ = client;
            using var stram = client.GetStream();
            using var reader = new StreamReader(stram);
            using var writer = new StreamWriter(stram) { NewLine = "\r\n", AutoFlush = true };
            try
            {
                var args = new List<string>();
                while (true)
                {
                    args.Clear();
                    var line = await reader.ReadLineAsync();
                    if (line == null) break;

                    if (line[0] != '*')
                        throw new Exception($"请以 * 开头 {line}为无效命令");

                    var argv = int.Parse(line.Substring(1));
                    for (int i = 0; i < argv; i++)
                    {
                        line = await reader.ReadLineAsync();
                        if (line == null || line[0] != '$')
                            throw new Exception($"请以 $ 开头并追加命令字节数 {line}无法解析");
                        var argLen = int.Parse(line.Substring(1));
                        line = await reader.ReadLineAsync();
                        if (line == null || line.Length != argLen)
                            throw new Exception($"{line}命令内容长度与$所示标记命令内容长度 不匹配");
                        args.Add(line);
                    }

                    var reply = ExecuteCommand(args);
                    if (reply == null)
                        await writer.WriteLineAsync("$-1");
                    else
                        await writer.WriteLineAsync($"${reply.Length}\r\n{reply}");
                }
            }
            catch (Exception ex)
            {
                try
                {
                    string? line;
                    var errReader = new StringReader(ex.ToString());
                    while ((line = errReader.ReadLine()) != null)
                    {
                        await writer.WriteAsync("\r\n-");
                        await writer.WriteLineAsync(line);
                    }
                    await writer.FlushAsync();
                }
                catch (Exception)
                {

                }
            }

        }

        /// <summary>
        /// 处理命令
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private string? ExecuteCommand(List<string> args)
        {
            switch (args[0].ToUpper())
            {
                case "GET":
                    return _state.GetValueOrDefault(args[1]);
                case "SET":
                    _state[args[1]] = args[2];
                    return null;
                default:
                    throw new Exception($"未知命令:{args[0]}");
            }
        }
    }

    /// <summary>
    /// redis server V0版本实现
    /// </summary>
    public class V0
    {
        private int _port;
        private TcpListener _listener;
        private RedisClone _redisClone;

        public V0(int port = 6379)
        {
            _port = port;
            _listener = new TcpListener(System.Net.IPAddress.Any, _port);
            _listener.Start();
            _redisClone = new RedisClone();
            Console.WriteLine($"V0 TCP正在监听{_port}");
        }

        public void Start()
        {
            while (true)
            {
                var client = _listener.AcceptTcpClient();
                _redisClone.HandleConnection(client); // run sync
            }
        }
    }

}
